7.3.5 Transacciones y Manejo de Errores
En una base de datos profesional, las cosas pueden fallar: se corta la luz, se pierde la conexión, o un dato es incorrecto. Si estamos realizando una operación compleja (como mover dinero de una cuenta a otra o mover un libro de una estantería a otra), no podemos permitir que la operación se quede a medias.
Aquí es donde entran las Transacciones y un robusto Manejo de Errores.
1️⃣ El Concepto: Atomicidad (ACID)
Una transacción es un conjunto de instrucciones que se ejecutan como una unidad atómica.
- O todo o nada: Si una sola instrucción falla, se deshacen todas las anteriores de ese bloque.
- Estado Consistente: La base de datos nunca se queda en un estado "intermedio" inválido.
🟩 commit() — La confirmación
Envía todos los cambios pendientes al disco de forma definitiva.
🟦 rollback() — La marcha atrás
Anula todos los cambios realizados desde el último commit. Es como un "Ctrl+Z" para la base de datos.
2️⃣ Gestión Manual de Transacciones
El patrón clásico consiste en envolver nuestras operaciones en un bloque try/except.
import sqlite3
conn = sqlite3.connect("biblioteca.db")
try:
cursor = conn.cursor()
# Operación 1: Quitar libro de la sección antigua
cursor.execute("UPDATE libros SET seccion = 'archivo' WHERE id = 1")
# Operación 2: Registrar el movimiento (imaginemos que esto falla)
cursor.execute("INSERT INTO movimientos (libro_id, destino) VALUES (?, ?)", (1, 'archivo'))
# Si todo ha ido bien, confirmamos
conn.commit()
print("✅ Transacción completada con éxito.")
except sqlite3.Error as e:
# Si algo falla, volvemos atrás para que el libro no se quede "en el limbo"
conn.rollback()
print(f"❌ Error en la transacción. Cambios revertidos: {e}")
finally:
conn.close()
3️⃣ Gestión Automática: El Context Manager
En Python, el objeto conexión puede usarse con la palabra clave with. Esto simplifica enormemente el código:
- Si el bloque
withtermina sin errores ➡️ hacecommit()automáticamente. - Si el bloque lanza cualquier excepción ➡️ hace
rollback()automáticamente.
with sqlite3.connect("biblioteca.db") as conn:
# Dentro de este bloque, la transacción está protegida
conn.execute("UPDATE libros SET stock = stock - 1 WHERE id = 1")
conn.execute("UPDATE ventas SET total = total + 10")
# No hace falta poner conn.commit(), ocurre solo al salir del 'with'
Fíjate que with conn: gestiona la transacción, pero no cierra la conexión. Debes cerrar el archivo al terminar con conn.close().
4️⃣ Jerarquía de Excepciones de SQLite
No todos los errores son iguales. Capturar la excepción correcta nos permite reaccionar mejor.
| Excepción | Cuándo ocurre |
|---|---|
sqlite3.Error | Clase base para todos los errores de SQLite. |
sqlite3.IntegrityError | Un valor repetido en un UNIQUE, un NULL donde no debía o una clave foránea que no existe. |
sqlite3.OperationalError | Errores externos: la tabla no existe, la base de datos está bloqueada por otro programa o el SQL tiene fallos de sintaxis. |
sqlite3.ProgrammingError | Error al usar la librería (ej: cerrar la conexión y luego intentar usar el cursor). |
5️⃣ El Problema de la Base de Datos Bloqueada
SQLite solo permite que un proceso escriba a la vez. Si tienes el archivo abierto en un visor de BD y a la vez intentas escribir desde Python, podrías recibir un error de "Database is locked".
Solución: Asegúrate de que tus transacciones sean lo más cortas posible y de cerrar siempre las conexiones.
✅ Buenas prácticas en Transacciones
- Transacciones Atómicas: Agrupa siempre las operaciones que "tengan sentido juntas". Si vendes un libro, debes bajar el stock y crear el recibo en la misma transacción.
- Manejo Específico: Captura
IntegrityErrorpara avisar al usuario de duplicados, pero deja que otros errores (OperationalError) se registren en un log de sistema. - Evita el Autocommit: No hagas un
commit()por cada línea si vas a insertar miles de registros; es preferible hacer uno solo al final para ganar velocidad. - Usa
with: Siempre que sea posible, delega la gestión del commit/rollback en el context manager de la conexión.
🧪 Ejercicios prácticos – Misión: La Cripta de Datos (Parte Final)
La biblioteca secreta se enfrenta a su mayor reto: un gran terremoto de datos está sacudiendo los cimientos digitales. Debes asegurar que el sistema sea indestructible.
🟢 Fase 1 – Blindaje Atómico
🟩 Ejercicio 1 – El Préstamo Seguro
Implementa una función prestar_libro(libro_id, socio_id) que realice dos operaciones en una única transacción:
- Cambie el estado del libro a 'prestado' en la tabla
libros. - Inserte un registro en una nueva tabla
prestamoscon la fecha y los IDs. Reto: Asegúrate de que si la inserción falla, el libro vuelva a estar 'disponible' automáticamente.
🟦 Ejercicio 2 – Simulacro de Fallo
Crea un script que intente insertar 10 libros nuevos dentro de un bloque with. Haz que el libro número 7 provoque un error de integridad (por ejemplo, repitiendo un título UNIQUE).
Verifica después si en la base de datos hay 0 libros nuevos o 6 libros. (Si lo has hecho bien, no debe haber ninguno).
🟡 Fase 2 – Diagnóstico y Limpieza
🟨 Ejercicio 3 – El Auditor de Errores
Crea un sistema que intente realizar varias operaciones prohibidas (borrar un autor con libros, insertar nulos, etc.). Captura cada error y guárdalo en un archivo de texto llamado errores_cripta.log con el siguiente formato:
[FECHA] - TIPO DE ERROR: [MENSAJE DEL ERROR]
🟧 Ejercicio 4 – La Cripta Corrupta
Ejecuta el script generador_caos.py de la carpeta de recursos. Este script creará una base de datos llamada cripta_corrupta.db llena de errores (páginas negativas, títulos nulos, tipos de datos erróneos).
Misión: Crea un script que recorra todos los libros de esa base de datos y, mediante transacciones y bloques try/except, intente "limpiarlos" guardándolos en una base de datos nueva y sana llamada cripta_sana.db.
🔴 Fase 3 – La Prueba de Fuego
🟥 Ejercicio 5 – El Migrador Masivo
Usa el archivo libros_migracion.json de la carpeta de recursos. Crea un script que lea los libros e intente insertarlos en bloque.
- La inserción debe ser ultrarrápida (usa un solo
commital final). - Como el JSON contiene un libro corrupto, la migración completa debe cancelarse automáticamente gracias al uso de un bloque
withde conexión.
🟪 Ejercicio 6 – La Cripta está a salvo
¡Enhorabuena! Has completado el manual. Para finalizar, crea un script "limpiador" que:
- Intente borrar todas las tablas de tu base de datos principal.
- Si falla por restricciones de clave foránea, que informe del error específico.
- Al finalizar, cierre todas las conexiones de forma segura.